﻿// -----------------------------------------------------------------------
// <copyright file="AdminHandler.cs" company="SoftServe">
// Panel for admin which can change some WebServer settings
// </copyright>
// -----------------------------------------------------------------------
namespace WebServer.BusinessLogic.Handlers
{
    using System;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using WebServer.BusinessLogic.Logs;
    using WebServer.BusinessLogic.Web;

    /// <summary>
    /// Handler to process changing settings 
    /// </summary>
    public class AdminHandler : Handler
    {
        /// <summary>
        /// path to admin log out page
        /// </summary>
        public const string AdminLogoutPage = "/admin/Logout.htm";

        /// <summary>
        /// path to admin settings page
        /// </summary>
        public const string AdminSettingsPage = "/admin/settings.html";

        /// <summary>
        /// path to admin error page
        /// </summary>
        protected const string AdminErrorPage = "/admin/error.html";

        /// <summary>
        /// path to admin logging page
        /// </summary>
        public const string AdminIndexPage = "/admin/index.html";

        /// <summary>
        /// path to admin logging sucess page
        /// </summary>
        protected const string AdminLoginSucessPage = "/admin/loggined.html";

        /// <summary>
        /// Session manager to use within this Handler
        /// </summary>
        private ISessionManager _sessionManager;

        /// <summary>
        /// Configurator to use within this Handler
        /// </summary>
        private Configurator _configurator;

        /// <summary>
        /// Initializes a new instance of the <see cref="AdminHandler" /> class.
        /// </summary>
        public AdminHandler()
        {
            this._sessionManager = SessionManager.Instance;
            this._configurator = Configurator.Instance;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AdminHandler" /> class.
        /// </summary>
        /// <param name="sessionManager">fake interface for testing</param>
        /// <param name="configurator">fake configurator for injecting</param>
        public AdminHandler(ISessionManager sessionManager, Configurator configurator)
            : this()
        {
            this._sessionManager = sessionManager;
            this._configurator = configurator;
        }

        /// <summary>
        /// Process http request
        /// </summary>
        /// <param name="request">http request</param>
        /// <returns>outcoming Response object</returns>
        public override Response Handle(Request request)
        {
            // get session
           var session = _sessionManager.GetCurrentSession(request);

            if (request.HttpMethod == EnumHttpMethod.POST)
            {
                if (request.HttpPath.EndsWith(AdminIndexPage, StringComparison.InvariantCultureIgnoreCase))
                {
                    //// POST /admin/index
                    var resp = new Response(request);

                    var postData = HttpUtility.ParseQueryString(request.Body);
                    if (postData.Count > 0)
                    {
                        if (postData["login"] == _configurator.AdminLogin &&
                            postData["password"] == _configurator.AdminPassword)
                        {
                            // remember session
                            session = _sessionManager.CreateSession(request, resp);

                            // update session as in "Admin mode"
                            session.IsAdmin = true;
                            _sessionManager.UpdateSession(request, session);

                            // log successful attempt
                            Logger.Instance.Log(string.Format(
                                                CultureInfo.CurrentCulture,
                                                              "-> Admin logged in with IP {0}. Session key is {1}",
                                                              request.UserIpAddress,
                                                              session.SessionKey));

                            // create temporary redirect, according to HTTP/1.0 specification
                            resp.SetRedirect(AdminSettingsPage, request);
                            return resp;
                        }
                    }

                    // redirect to error page
                    resp.SetRedirect(AdminErrorPage, request);
                    return resp;
                }
                else if (request.HttpPath.EndsWith(AdminSettingsPage, StringComparison.InvariantCultureIgnoreCase))
                {
                    // POST /admin/settings, to save settings
                    if (session != null && session.IsAdmin)
                    {
                        string dataLine =
                            request.RawData.Substring(
                                request.RawData.IndexOf("\r\n\r\n", StringComparison.InvariantCultureIgnoreCase) + 4);
                        NameValueCollection parameters =
                                                    System.Web.HttpUtility.ParseQueryString(dataLine);
                        
                        // Setting Port
                        int port = -1;
                        int.TryParse(parameters["port"], out port);
                        if (port > 0)
                        {
                            _configurator.Port = port;
                        }

                        // setting MaxUsers
                        int users = -1;
                        int.TryParse(parameters["maxusers"], out users);
                        if (users > 0)
                        {
                            _configurator.MaxUsers = users;
                        }

                        // setting Load balancer port
                        int balancerPort = -1;
                        int.TryParse(parameters["loadBalancerAnsweringPort"], out balancerPort);
                        if (balancerPort > 0)
                        {
                            _configurator.LoadBalancerAnsweringPort = balancerPort;
                        }

                        balancerPort = -1;
                        int.TryParse(parameters["loadBalancerAskingPort"], out balancerPort);
                        if (balancerPort > 0)
                        {
                            _configurator.LoadBalancerAskingPort = balancerPort;
                        }

                        // setting WwwPath
                        string wwwPathLine = parameters["relativewwwpath"];
                        if (!string.IsNullOrEmpty(wwwPathLine))
                        {
                            _configurator.RelativeWwwPath = System.Web.HttpUtility.UrlDecode(wwwPathLine);
                        }

                        // setting LogPath
                        string logPathLine = parameters["loggerpath"];
                        if (!string.IsNullOrEmpty(logPathLine) && !logPathLine.StartsWith("\0"))
                        {
                            _configurator.LoggerPath = System.Web.HttpUtility.UrlDecode(logPathLine);
                        }

                        // setting server name
                        string serverNameLine = parameters["servername"];
                        if (!string.IsNullOrEmpty(serverNameLine) && !serverNameLine.StartsWith("\0"))
                        {
                            _configurator.ServerName = serverNameLine;
                        }

                        // setting server mode
                        if (dataLine.Contains("mode"))
                        {
                            switch (parameters["mode"].Substring(0, 4))
                            {
                                case "rad1":
                                    _configurator.ServerMode = ServerConfiguration.Normal;
                                    break;
                                case "rad2":
                                    _configurator.ServerMode = ServerConfiguration.Redirect;
                                    break;
                                case "rad3":
                                    _configurator.ServerMode = ServerConfiguration.LoadBalancer;
                                    break;
                                default:
                                    _configurator.ServerMode = _configurator.ServerMode;
                                    break;
                            }
                        }

                        // cookieless server mode
                        if (dataLine.Contains("cookieless"))
                        {
                            switch (parameters["cookieless"].Substring(0, 4))
                            {
                                case "rad1":
                                    _configurator.IsCookielessSession = true;
                                    break;
                                case "rad2":
                                    _configurator.IsCookielessSession = false;
                                    break;
                                default:
                                    _configurator.IsCookielessSession = false;
                                    break;
                            }
                        }

                       
                        // string filePath = Configurator.Instance.RelativeWwwPath + "\\admin\\Loggined.html";
                        // string text = File.ReadAllText(filePath);
                        // byte[] data = this.TextToByteArray(text, Encoding.ASCII);
                        // string MIMEType = "text/html";
                        // string version = request.HttpVersion;
                        // return new Response(MIMEType, version, data);
                        // save data to the file
                        ConfiguratorSerializer.Save(_configurator);

                        var sessionString = _sessionManager.GetSessionUrl(request);
                        if (!String.IsNullOrEmpty(sessionString))
                        {
                            sessionString = "/" + sessionString;
                        }

                        Regex regex = new Regex(@":\d+/?$");
                        string host = regex.Replace(request.Host, ":" + _configurator.Port) + sessionString + request.HttpPath;
                        string webPage = "<html><head><title>Success</title><META HTTP-EQUIV=\"REFRESH\" CONTENT=\"5;URL=\'http://" + host + "'\"></head><body><p>You will be redirected shorly...</p></body></html>";
                        MyHttpServer.Restart();
                        return new Response("text/html", request.HttpVersion, Encoding.UTF8.GetBytes(webPage));
                        // TODO: move to another, more informative page?
                        // request.HttpPath = AdminSettingsPage;
                    }
                }
            }

            if (request.HttpMethod == EnumHttpMethod.GET)
            {
                if (request.HttpPath == AdminIndexPage)
                {
                    if (session != null && session.IsAdmin == true)
                    {
                        var resp = new Response();
                        resp.SetRedirect(AdminSettingsPage, request);
                        return resp;

                        //request.HttpPath = AdminSettingsPage;
                    }
                    else
                    {
                        return this.NextHandler.Handle(request);
                    }
                }
                // disable acces to certain files, when not logged in
                // TODO: move this to custom HttpAccess handler and read from files
                if (request.HttpPath.EndsWith(AdminSettingsPage, StringComparison.InvariantCultureIgnoreCase) && (session == null || !session.IsAdmin))
                {
                    var resp = new Response(request);
                    resp.SetAccessDenied();
                    return resp;
                }

                // this is not real file, "admin/Logout" is a command for server to log out current user
                if (request.HttpPath.EndsWith(AdminLogoutPage, StringComparison.InvariantCultureIgnoreCase))
                {
                    // we have to create new response, because cookies can only be deleted on response
                    // if we move to next handler, we've only got request
                    var resp = new Response();
                    resp.SetRedirect(AdminIndexPage, request);
                    //resp.Data = Encoding.UTF8.GetBytes("You have been logged out");

                    // prepare session for removal (in response)
                    if (session != null)
                    {
                        _sessionManager.DeleteSession(session.SessionKey, request, resp);

                        // log logging out process
                        Logger.Instance.Log(string.Format(CultureInfo.CurrentCulture, "<- Admin logged out with IP {0}", request.UserIpAddress));
                    }
                    else
                    {
                        resp.Data = Encoding.UTF8.GetBytes("Logging out failed, because no session was specified.");
                    }

                    // return session removal request
                    return resp;
                }

            }

            return this.NextHandler.Handle(request);
        }
    }
}
